package crypter

import (
	"crypto/cipher"
	"crypto/des"
	"encoding/base64"
	"encoding/hex"
	"errors"

	"gitee.com/gousing/helper/internal"
)

const (
	DesErrDataEmpty  = "DES data invalid: cannot be empty"
	DesErrDataDecode = "DES data invalid: can't decode"
	DesErrKey        = "DES key invalid: must be 8 bytes long"
	DesErrIV         = "DES iv invalid: must be 8 bytes long"
)

// DesEncryptCBC 字节数据加密 (DES/CBC/PKCS7Padding)
//   - data 待加密的数据
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回加密后的字节数组
func DesEncryptCBC(data []byte, key []byte, iv []byte) (encData []byte, err error) {
	if err = desValidData(&data); err != nil {
		return
	}
	if err = desValidKey(&key); err != nil {
		return
	}
	if err = desValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = des.NewCipher(key)
	if err != nil {
		err = errors.New(DesErrKey)
		return
	}
	// 获取秘钥块的长度
	blockSize := block.BlockSize()
	// 加密模式
	blockMode := cipher.NewCBCEncrypter(block, iv)
	// 填充补全码
	paddingData := PKCS7Padding(data, blockSize)
	// 创建数组
	encData = make([]byte, len(paddingData))
	// 加密
	blockMode.CryptBlocks(encData, paddingData)
	return encData, nil
}

// DesEncryptCBCHex 字符串数据加密 (DES/CBC/PKCS7Padding)
//   - data 待加密的字符串
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回 Hex 16进制格式的加密结果(小写)
func DesEncryptCBCHex(data string, key string, iv string) (string, error) {
	dataBytes, err := DesEncryptCBC(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(dataBytes), nil
}

// DesEncryptCBCBase64 字符串数据加密 (DES/CBC/PKCS7Padding)
//   - data 待加密的字符串
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回 Base64格式的加密结果
func DesEncryptCBCBase64(data string, key string, iv string) (string, error) {
	dataBytes, err := DesEncryptCBC(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(dataBytes), nil
}

// DesDecryptCBC 字节数组数据解密 (DES/CBC/PKCS7Padding)
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回解密后的字节数组
func DesDecryptCBC(data []byte, key []byte, iv []byte) (decData []byte, err error) {
	if err = desValidData(&data); err != nil {
		return
	}
	if err = desValidKey(&key); err != nil {
		return
	}
	if err = desValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = des.NewCipher(key)
	if err != nil {
		//AES Key 字符长度必须为8
		err = errors.New(DesErrKey)
		return
	}
	// 获取秘钥块的长度(AES Block Size固定为16位)
	blockSize := block.BlockSize()
	// 解密模式
	blockMode := cipher.NewCBCDecrypter(block, iv)
	// 解密数据
	decData = make([]byte, len(data))
	// 解密
	blockMode.CryptBlocks(decData, data)
	// 移除补全码
	decData = PKCS7UnPadding(decData, blockSize)
	return decData, nil
}

// DesDecryptCBCHex  解密字符串数据 (DES/CBC/PKCS7Padding)
//   - data 待解密的字符串(Hex 16进制格式的加密结果(小写))
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回解密后的字符串
func DesDecryptCBCHex(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(DesErrDataEmpty)
	}
	dataBytes, err := hex.DecodeString(data)
	if err != nil {
		return "", errors.New(DesErrDataDecode)
	}
	decBytes, err := DesDecryptCBC(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), nil
}

// DesDecryptCBCBase64   解密字符串数据 (DES/CBC/PKCS7Padding)
//   - data 待解密的字符串(Base64格式)
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回解密后的字符串
func DesDecryptCBCBase64(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(DesErrDataEmpty)
	}
	dataBytes, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		return "", errors.New(DesErrDataDecode)
	}
	decBytes, err := DesDecryptCBC(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), nil
}

// DesEncryptCFB 数据加密 (DES/CFB/NoPadding)
//   - data 待加密的数据
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回加密后的数据
func DesEncryptCFB(data []byte, key []byte, iv []byte) (encData []byte, err error) {
	if err = desValidData(&data); err != nil {
		return
	}
	if err = desValidKey(&key); err != nil {
		return
	}
	if err = desValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = des.NewCipher(key)
	if err != nil {
		err = errors.New(DesErrKey)
		return
	}
	// 加密
	encrypter := cipher.NewCFBEncrypter(block, iv)
	encData = make([]byte, len(data))
	encrypter.XORKeyStream(encData, data)
	return encData, nil
}

// DesEncryptCFBHex 字符串数据加密 (DES/CFB/NoPadding)
//   - data 待加密的字符串
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回 Hex 16进制格式的加密结果(小写)
func DesEncryptCFBHex(data string, key string, iv string) (string, error) {
	encData, err := DesEncryptCFB(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(encData), nil
}

// DesEncryptCFBBase64 字符串数据加密 (DES/CFB/NoPadding)
//   - data 待加密的字符串
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回 Base64格式的加密结果
func DesEncryptCFBBase64(data string, key string, iv string) (string, error) {
	encData, err := DesEncryptCFB(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(encData), nil
}

// DesDecryptCFB 数据解密 (DES/CFB/NoPadding)
//   - data 待解密的数据
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回解密后的数据
func DesDecryptCFB(data []byte, key []byte, iv []byte) (decData []byte, err error) {
	if err = desValidData(&data); err != nil {
		return
	}
	if err = desValidKey(&key); err != nil {
		return
	}
	if err = desValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = des.NewCipher(key)
	if err != nil {
		err = errors.New(DesErrKey)
		return
	}
	// 解密
	decrypter := cipher.NewCFBDecrypter(block, iv)
	decData = make([]byte, len(data))
	decrypter.XORKeyStream(decData, data)
	return decData, nil
}

// DesDecryptCFBHex   解密字符串数据 (DES/CFB/NoPadding)
//   - data 待解密的字符串(Hex 16进制格式的加密结果(小写))
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回解密后的字符串
func DesDecryptCFBHex(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(DesErrDataEmpty)
	}
	dataBytes, err := hex.DecodeString(data)
	if err != nil {
		return "", errors.New(DesErrDataDecode)
	}
	decBytes, err := DesDecryptCFB(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), err
}

// DesDecryptCFBBase64  解密字符串数据(DES/CFB/NoPadding)
//   - data 待解密的字符串(Base64格式)
//   - key 秘钥, 8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - iv 向量，8个字节的长度，DES Block Size固定为8位，超过8位时截取前8位
//   - 返回解密后的字符串
func DesDecryptCFBBase64(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(DesErrDataEmpty)
	}
	dataBytes, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		return "", errors.New(DesErrDataDecode)
	}
	decBytes, err := DesDecryptCFB(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), err
}

func desValidData(data *[]byte) error {
	if len(*data) < 1 {
		return errors.New(DesErrDataEmpty)
	}
	return nil
}
func desValidKey(key *[]byte) error {
	if len(*key) < 8 {
		return errors.New(DesErrKey)
	} else if len(*key) > 8 {
		*key = (*key)[0:8]
	}
	return nil
}
func desValidIV(iv *[]byte) error {
	if len(*iv) < 8 {
		return errors.New(DesErrIV)
	} else if len(*iv) > 8 {
		*iv = (*iv)[0:8]
	}
	return nil
}
